/*
 * common.cpp
 *
 *  Created on: Aug 12, 2016
 *      Author: ubuntu
 */

#include "common.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <openssl/md5.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>


static char logfile[255] = { 0 };
static int debuglevel = 0;

static unsigned short crc16_table[256] = {
	0x0000,0xc0c1,0xc181,0x0140,0xc301,0x03c0,0x0280,0xc241,
	0xc601,0x06c0,0x0780,0xc741,0x0500,0xc5c1,0xc481,0x0440,
	0xcc01,0x0cc0,0x0d80,0xcd41,0x0f00,0xcfc1,0xce81,0x0e40,
	0x0a00,0xcac1,0xcb81,0x0b40,0xc901,0x09c0,0x0880,0xc841,
	0xd801,0x18c0,0x1980,0xd941,0x1b00,0xdbc1,0xda81,0x1a40,
	0x1e00,0xdec1,0xdf81,0x1f40,0xdd01,0x1dc0,0x1c80,0xdc41,
	0x1400,0xd4c1,0xd581,0x1540,0xd701,0x17c0,0x1680,0xd641,
	0xd201,0x12c0,0x1380,0xd341,0x1100,0xd1c1,0xd081,0x1040,
	0xf001,0x30c0,0x3180,0xf141,0x3300,0xf3c1,0xf281,0x3240,
	0x3600,0xf6c1,0xf781,0x3740,0xf501,0x35c0,0x3480,0xf441,
	0x3c00,0xfcc1,0xfd81,0x3d40,0xff01,0x3fc0,0x3e80,0xfe41,
	0xfa01,0x3ac0,0x3b80,0xfb41,0x3900,0xf9c1,0xf881,0x3840,
	0x2800,0xe8c1,0xe981,0x2940,0xeb01,0x2bc0,0x2a80,0xea41,
	0xee01,0x2ec0,0x2f80,0xef41,0x2d00,0xedc1,0xec81,0x2c40,
	0xe401,0x24c0,0x2580,0xe541,0x2700,0xe7c1,0xe681,0x2640,
	0x2200,0xe2c1,0xe381,0x2340,0xe101,0x21c0,0x2080,0xe041,
	0xa001,0x60c0,0x6180,0xa141,0x6300,0xa3c1,0xa281,0x6240,
	0x6600,0xa6c1,0xa781,0x6740,0xa501,0x65c0,0x6480,0xa441,
	0x6c00,0xacc1,0xad81,0x6d40,0xaf01,0x6fc0,0x6e80,0xae41,
	0xaa01,0x6ac0,0x6b80,0xab41,0x6900,0xa9c1,0xa881,0x6840,
	0x7800,0xb8c1,0xb981,0x7940,0xbb01,0x7bc0,0x7a80,0xba41,
	0xbe01,0x7ec0,0x7f80,0xbf41,0x7d00,0xbdc1,0xbc81,0x7c40,
	0xb401,0x74c0,0x7580,0xb541,0x7700,0xb7c1,0xb681,0x7640,
	0x7200,0xb2c1,0xb381,0x7340,0xb101,0x71c0,0x7080,0xb041,
	0x5000,0x90c1,0x9181,0x5140,0x9301,0x53c0,0x5280,0x9241,
	0x9601,0x56c0,0x5780,0x9741,0x5500,0x95c1,0x9481,0x5440,
	0x9c01,0x5cc0,0x5d80,0x9d41,0x5f00,0x9fc1,0x9e81,0x5e40,
	0x5a00,0x9ac1,0x9b81,0x5b40,0x9901,0x59c0,0x5880,0x9841,
	0x8801,0x48c0,0x4980,0x8941,0x4b00,0x8bc1,0x8a81,0x4a40,
	0x4e00,0x8ec1,0x8f81,0x4f40,0x8d01,0x4dc0,0x4c80,0x8c41,
	0x4400,0x84c1,0x8581,0x4540,0x8701,0x47c0,0x4680,0x8641,
	0x8201,0x42c0,0x4380,0x8341,0x4100,0x81c1,0x8081,0x4040
};

static char base_table[64] = {
	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
	'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
	'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
	'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
	'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
	'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
	'w', 'x', 'y', 'z', '0', '1', '2', '3',
	'4', '5', '6', '7', '8', '9', '+', '/'
};

void set_debuglevel(int level) {
	if (debuglevel >= 0)
		debuglevel = level;
}

void set_logfile(const char *name)
{
	memset(logfile, 0, sizeof(logfile));
	strncpy(logfile, name, sizeof(logfile));
}

void _debug(int level, const char *file, const int line, const char *fmt, ...)
{
	FILE *fp = NULL;
	if (debuglevel < level)
		return;

	if (strlen(logfile) == 0) {
		fp = stdout;
	} else {
		fp = fopen(logfile, "a");
	}
	if (!fp)
		return;

	time_t ts = time(NULL);
	struct tm tm;
	localtime_r(&ts, &tm);
	char timebuf[30];
	strftime(timebuf, sizeof(timebuf), "%F %H:%M:%S", &tm);
	fprintf(fp, "%s [%s:%d] ", timebuf, file, line);
	va_list ap;
	va_start(ap, fmt);
	vfprintf(fp, fmt, ap);
	va_end(ap);
	fputc('\n', fp);
	fflush(fp);

	if (fp != stdout)
		fclose(fp);
}

void debughex(int level, unsigned char *buf, size_t len)
{
	if (debuglevel < level)
		return;
}

void update_logfile()
{
	if (strlen(logfile) == 0)
		return;

	struct stat buf;
	if (stat(logfile, &buf) == -1) {
		return;
	}

	if (buf.st_size < 104857600) {
		return;
	}

	char file[50];
	struct custime cus = getNow();
	snprintf(file, sizeof(file), "log%04d%02d%02d", cus.year, cus.month, cus.day);
	if (strncmp(file, logfile, strlen(file)) != 0) {
		strcpy(logfile, file);
	} else {
		snprintf(logfile, sizeof(logfile), "log%04d%02d%02d%02d", cus.year, cus.month, cus.day, cus.hour);
	}
}

static inline unsigned short crc16_byte(unsigned short crc, unsigned char data)
{
	return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xFF];
}

char basechar(int i)
{
	return base_table[i&0x3F];
}

struct custime getNow()
{
	struct custime cus;
	time_t ts = time(NULL);
	struct tm tm;
	localtime_r(&ts, &tm);
	cus.year = tm.tm_year + 1900;
	cus.month = tm.tm_mon + 1;
	cus.day = tm.tm_mday;
	cus.hour = tm.tm_hour;
	cus.min = tm.tm_min;
	cus.sec = tm.tm_sec;
	return cus;
}

int chtoi(char c)
{
	return (c & 0x40) ? ((c & 0xF) + 9) : (c & 0xF);
}

int commonth(struct custime t1, struct custime t2)
{
	if (t1.year != t2.year)
		return (t1.year - t2.year);
	if (t1.month != t2.month)
		return (t1.month - t2.month);
	return 0;
}

int comtime(struct custime t1, struct custime t2)
{
	if (t1.year != t2.year)
		return (t1.year-t2.year);
	if (t1.month != t2.month)
		return (t1.month - t2.month);
	if (t1.day != t2.day)
		return (t1.day - t2.day);
	if (t1.hour != t2.hour)
		return (t1.hour - t2.hour);
	if (t1.min != t2.min)
			return (t1.min - t2.min);
	if (t1.sec != t2.sec)
			return (t1.sec - t2.sec);

	return 0;
}

void addtime(struct custime *t)
{
	int year = t->year;
	int month = t->month;
	int day = t->day;
	switch (month) {
	case 1:
	case 3:
	case 5:
	case 7:
	case 8:
	case 10:
	case 12:
		{
			if (day < 31)
			{
				day++;
			} else {
				year = (month == 12) ? (year+1):year;
				month = (month == 12) ? 1:(month+1);
				day = 1;
			}
		}
		break;
	case 4:
	case 6:
	case 9:
	case 11:
		{
			if (day < 30) {
				day++;
			} else {
				day = 1;
				month++;
			}

		}
		break;
	case 2:
		{
			if ((year % 4) == 0) {
				if ((year % 100) == 0 && (year % 400) != 0) {
					if (day < 28) {
						day++;
					}else {
						day = 1;
						month++;
					}
				} else {
					if (day < 29) {
						day++;
					} else {
						day = 1;
						month++;
					}
				}
			} else {
				if (day < 28) {
					day++;
				} else {
					day = 1;
					month++;
				}
			}
		}
		break;
	}
	t->year = year;
	t->month = month;
	t->day = day;
}

void deltime(struct custime *t)
{
	int year = t->year;
	int month = t->month;
	int day = t->day;
	switch (month) {
	case 1:
	case 5:
	case 7:
	case 8:
	case 10:
	case 12:
		{
			if (day > 1) {
				day--;
			} else {
				if (month == 8) {
					month--;
					day = 31;
				} else if (month == 1) {
					month = 12;
					day = 31;
					year--;
				} else {
					month--;
					day = 30;
				}
			}
		}
		break;
	case 2:
	case 4:
	case 6:
	case 9:
	case 11:
		{
			if (day > 1) {
				day--;
			} else {
				day = 31;
				month--;
			}
		}
		break;
	case 3:
		{
			if (day > 1) {
				day--;
				break;
			}
			if ((year % 4) == 0) {
				if ((year % 100)==0 && (year % 400) != 0)  {
					day = 28;
					month = 2;
				} else {
					day = 29;
					month = 2;
				}
			} else {
				day = 28;
				month--;
			}
		}
		break;
	}
	t->year = year;
	t->month = month;
	t->day = day;
}

int mday(int month, int year) {
    switch (month) {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
            return 31;
        case 4:
        case 6:
        case 9:
        case 11:
            return 30;
        default:
            {
                if (year % 4 == 0) {
                    if (year % 100 == 0) {
                        if (year % 400 == 0) {
                            return 29;
                        }else {
                            return 28;
                        }
                    } else {
                        return 29;
                    }
                } else {
                    return 28;
                }
            }
    }
}

int yday(int year) {
    if (year % 4 == 0) {
        if (year % 100 == 0) {
            if (year % 400 == 0) {
                return 366;
            } else {
                return 365;
            }
        } else {
            return 366;
        }
    } else {
        return 365;
    }
}

int diftime(struct custime t1, struct custime t2)
{
	struct custime tmax, tmin;
	int dmon = 0, dday =0, dyear = 0;
	int res = comtime(t1, t2);
	tmax = (res > 0) ? t1 : t2;
	tmin = (res > 0) ? t2 : t1;

	if (res == 0)
		return 0;

	dyear = tmax.year - tmin.year;
	if (dyear > 0) {
		dday = mday(tmin.month, tmin.year) - tmin.day;
		tmin.month ++;
		while (tmin.month < 12) {
			dday += mday(tmin.month, tmin.year);
			tmin.month++;
		}
		tmin.year++;
		while (tmin.year < tmax.year) {
			dday += yday(tmin.year);
			tmin.year++;
		}
		dmon = 1;
		while (dmon < tmax.month) {
			dday += mday(dmon, tmax.year);
			dmon++;
		}
		dday += tmax.day;
	} else {
		dmon = tmax.month - tmin.month;
		if (dmon > 0) {
			dday = mday(tmin.month, tmin.year) - tmin.day;
			tmin.month ++;
			while (tmin.month < tmax.month) {
				dday += mday(tmin.month, tmin.year);
				tmin.month++;
			}
			dday += tmax.day;
		} else {
			dday = tmax.day - tmin.day;
		}
	}
	return dday;
}

time_t
strtotime(const char *timestr, const char *format)
{
	struct tm tm;
	strptime(timestr, format, &tm);
	return mktime(&tm);
}

time_t
custotime(struct custime cus)
{
	struct tm tm;
	if (cus.year < 1970)
	{
		return 0;
	}
	tm.tm_year = cus.year - 1900;
	tm.tm_mon = cus.month - 1;
	tm.tm_mday = cus.day;
	tm.tm_hour = cus.hour;
	tm.tm_min = cus.min;
	tm.tm_sec = cus.sec;
	return mktime(&tm);
}

struct tm
custotm(struct custime cus, struct tm *tm)
{
	if (cus.year < 1970)
	{
		time_t ts  = 0;
		localtime_r(&ts, tm);
		return *tm;
	}
	tm->tm_year = cus.year-1900;
	tm->tm_mon = cus.month-1;
	tm->tm_mday = cus.day;
	tm->tm_hour = cus.hour;
	tm->tm_min = cus.min;
	tm->tm_sec = cus.sec;
	return *tm;
}

struct custime
timetocstm(const time_t timep)
{
	struct custime cus;
	struct tm tm;
	localtime_r(&timep, &tm);
	cus.year = tm.tm_year + 1900;
	cus.month = tm.tm_mon + 1;
	cus.day = tm.tm_mday;
	cus.hour = tm.tm_hour;
	cus.min = tm.tm_min;
	cus.sec = tm.tm_sec;
	return cus;
}

string
dateftime(const time_t timep, const char *format)
{
	struct tm tm;
	char timebuf[100];
	localtime_r(&timep, &tm);
	strftime(timebuf, sizeof(timebuf), format, &tm);
	return timebuf;
}

string str_difftime(time_t diftime)
{
	int day, hour, min, sec;
	char timebuf[100];
	if (diftime < 60) {
		snprintf(timebuf, sizeof(timebuf), "%lu秒", diftime);
		return timebuf;
	}

	if (diftime < 3600) {
		sec = diftime % 60;
		min = diftime / 60;
		snprintf(timebuf, sizeof(timebuf), "%d分%d秒", min, sec);
		return timebuf;
	}

	if (diftime < 24 * 3600) {
		hour = diftime /  3600;
		min = (diftime - hour * 3600) / 60;
		sec = diftime % 60;
		snprintf(timebuf, sizeof(timebuf), "%d时%d分%d秒", hour, min, sec);
		return timebuf;
	}

	day = diftime / (24 * 3600);
	diftime -= day * 24 * 3600;
	hour = diftime / 3600;
	diftime -= hour * 3600;
	min = diftime / 60;
	sec = diftime % 60;
	snprintf(timebuf, sizeof(timebuf), "%d天%d时%d分%d秒", day, hour, min, sec);

	return timebuf;
}

unsigned short crc16(unsigned short crc, unsigned char *buffer, size_t len)
{
	while (len--)
		crc = crc16_byte(crc, *buffer++);
	return crc;
}

int base64_encode(char *dst, size_t dlen, const unsigned char *src, size_t len)
{
	int n = 0;
	int block = len / 3;
	int remainder = len % 3;
	int i, k1, k2, k3, k4, tmp;
	n += snprintf(dst, dlen, "-----BEGIN PUBLIC KEY-----\n");
	for (i = 0; i < block; i++) {
		tmp = src[3*i] << 16 | src[3*i+1] << 8 | src[3*i+2];
		k1 = (tmp >> 18) & 0x3F;
		k2 = (tmp >> 12) & 0x3F;
		k3 = (tmp >> 6) & 0x3F;
		k4 = tmp  & 0x3F;
		if ((i+1) % 16 == 0) {
			n += snprintf(dst+n, dlen-n, "%c%c%c%c\n", base_table[k1],
					base_table[k2], base_table[k3], base_table[k4]);
		} else {
			n += snprintf(dst+n, dlen-n, "%c%c%c%c", base_table[k1],
					base_table[k2], base_table[k3], base_table[k4]);
		}
	}

	if (remainder == 1) {
		k1 = (src[len-1] >> 2) & 0x3F;
		k2 = (src[len-1] & 0x3) << 4;
		n += snprintf(dst+n, dlen-n, "%c%c==\n", base_table[k1],
				base_table[k2]);
	} else {
		k1 = (src[len-2] >> 2) & 0x3F;
		k2 = (src[len-2] & 0x3) << 4 | (src[len-1] >> 4 & 0xF);
		k3 = (src[len-1] & 0xF) << 2;
		n += snprintf(dst+n, dlen-n, "%c%c%c=\n", base_table[k1],
				base_table[k2], base_table[k3]);
	}

	n += snprintf(dst+n, dlen-n, "-----END PUBLIC KEY-----\n");
	return n;
}

int
connect_nonb(int sockfd, struct sockaddr *saptr, socklen_t salen, int nsec)
{
        int flags, n, error;
        socklen_t len;
        fd_set rset, wset;
        struct timeval tval;

        flags = fcntl(sockfd, F_GETFL, 0);
        fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

        error = 0;
        if ((n = connect(sockfd, saptr, salen)) < 0)
                if (errno != EINPROGRESS)
                        return -1;

        if (n == 0)
                goto done;

        FD_ZERO(&rset);
		FD_SET(sockfd, &rset);
        wset = rset;
        tval.tv_sec = nsec;
        tval.tv_usec = 0;

        if ((n = select(sockfd + 1, &rset, &wset, NULL,
                                        nsec ? &tval : NULL)) == 0) {
                close(sockfd);
                errno = ETIMEDOUT;
                return -1;
        }

        if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
                len = sizeof(error);
                if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) > 0)
                        return -1;
        } else {
                return -1;
        }
done:
        fcntl(sockfd, F_SETFL, flags);
        if (error) {
		close(sockfd);
                errno = error;
                return -1;
        }
        return 0;
}

void addmonth(struct custime *t)
{
	int year = t->year;
	int month = t->month;
	switch (month) {
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
	case 6:
	case 7:
	case 8:
	case 9:
	case 10:
	case 11:
		{
			month++;
		}
		break;
	case 12:
		{
			year++;
			month = 1;
		}
		break;
	}
	t->year = year;
	t->month = month;
}

int connect_server(const char *server, int port, int sec)
{
	int fd = socket(AF_INET, SOCK_STREAM, 0);

	if (fd < 0) {
		return -1;
	}

	struct sockaddr_in addr;
	bzero(&addr, sizeof(addr));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	if (server == NULL)
		addr.sin_addr.s_addr = INADDR_ANY;
	else
		addr.sin_addr.s_addr = inet_addr(server);

	if (connect_nonb(fd, (struct sockaddr *)&addr, sizeof(addr), sec) < 0) {
		close(fd);
		return -1;
	}

	return fd;
}


void delmonth(struct custime *t)
{
	if (t->month > 1)
		t->month--;
	else {
		t->year--;
		t->month = 12;
	}
}

int md5(const char *file, unsigned char *md)
{
	MD5_CTX ctx;
	char buf[1024];
	int n;
	if (!file || !md)
		return -1;
	FILE *fp = fopen(file, "r");
	if (!fp)
		return -1;
	MD5_Init(&ctx);
	while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
		MD5_Update(&ctx, buf, n);
	}
	fclose(fp);
	MD5_Final(md, &ctx);

	return 1;
}

double strtodouble(unsigned char *str)
{
	double d = 0;
#if BYTE_ORDER == LITTLE_ENDIAN
	d = *(double *)str;
#else
	int i, k;
	unsigned char tmp[10];
	k = 0;
	for (i = 0; i < 8; i++)
		tmp[k++] = str[7-i];
	d = *(double *)tmp;
#endif
	return d;
}

int setnonblocking(int fd)
{
	return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, NULL)|O_NONBLOCK);
}

int memsearch(const unsigned char *s, const unsigned char *tag, size_t len)
{
	if (len == 0)
		return -1;
	size_t pos = 0;
	unsigned char *p = NULL;
	while (pos < len) {
		p = (unsigned char *)memchr(s, tag[0], len);
		if (!p)
			return -1;
		if (memcmp(p, tag, len) == 0)
		{
			pos = p - s;
			break;
		}
		pos += p+1 - s;
		p++;
	}

	return pos;
}
